/*
 * @Description: 用于实现RT-Thread的I2C驱动
 * @Author: Dryad
 * @Date: 2019-05-25 10:24:08
 */
#include <rtdevice.h>
#include <rthw.h>
#include <stm32f4xx_gpio.h>

struct stm32_i2c_gpio_t
{
    GPIO_TypeDef *gpio;
    uint16_t pin;
};

__STATIC_INLINE void GPIO_Set(struct stm32_i2c_gpio_t *gpio);
__STATIC_INLINE void GPIO_Clear(struct stm32_i2c_gpio_t *gpio);
__STATIC_INLINE uint32_t GPIO_Read(struct stm32_i2c_gpio_t *gpio);

void GPIO_Set(struct stm32_i2c_gpio_t *gpio)
{
    gpio->gpio->BSRRL = gpio->pin;
}

void GPIO_Clear(struct stm32_i2c_gpio_t *gpio)
{
    gpio->gpio->BSRRH = gpio->pin;
}

uint32_t GPIO_Read(struct stm32_i2c_gpio_t *gpio)
{
    return ((gpio->gpio->IDR) & gpio->pin);
}

static void stm32_i2c_set_sda(void *data, rt_int32_t state);
static void stm32_i2c_set_scl(void *data, rt_int32_t state);
static rt_int32_t stm32_i2c_get_sda(void *data);
static rt_int32_t stm32_i2c_get_scl(void *data);
rt_inline void stm32_i2c_udelay(rt_uint32_t us);

static void stm32_init_i2c_gpio(struct stm32_i2c_gpio_t *gpio); /* 用于初始化i2c的gpio */

struct drv_i2c_gpio
{
    struct stm32_i2c_gpio_t sda;
    struct stm32_i2c_gpio_t scl;
};

void stm32_i2c_set_sda(void *data, rt_int32_t state)
{
    struct drv_i2c_gpio *i2c_gpio = (struct drv_i2c_gpio *)data;

    if (state)
    {
        GPIO_Set(&i2c_gpio->sda);
    }
    else
    {
        GPIO_Clear(&i2c_gpio->sda);
    }
}

void stm32_i2c_set_scl(void *data, rt_int32_t state)
{
    struct drv_i2c_gpio *i2c_gpio = (struct drv_i2c_gpio *)data;
    if (state)
    {
        GPIO_Set(&i2c_gpio->scl);
    }
    else
    {
        GPIO_Clear(&i2c_gpio->scl);
    }
}

rt_int32_t stm32_i2c_get_sda(void *data)
{
    struct drv_i2c_gpio *i2c_gpio = (struct drv_i2c_gpio *)data;

    rt_int32_t result;
    result = (GPIO_Read(&i2c_gpio->sda)) ? 1 : 0;
    return result;
}

rt_int32_t stm32_i2c_get_scl(void *data)
{
    struct drv_i2c_gpio *i2c_gpio = (struct drv_i2c_gpio *)data;

    rt_int32_t result;
    result = (GPIO_Read(&i2c_gpio->scl)) ? 1 : 0;

    return result;
}

void stm32_i2c_udelay(rt_uint32_t us)
{
    rt_hw_us_delay(us);
}

#ifdef RT_USING_I2C1
struct drv_i2c_gpio i2c1_gpio = {
    .sda = {
        .gpio = GPIOA,
        .pin = GPIO_Pin_1,
    },
    .scl = {
        .gpio = GPIOA,
        .pin = GPIO_Pin_0,
    },
};

const struct rt_i2c_bit_ops i2c1_gpio_bit_ops =
    {
        .data = (void *)(&i2c1_gpio),
        .set_sda = stm32_i2c_set_sda,
        .set_scl = stm32_i2c_set_scl,
        .get_sda = stm32_i2c_get_sda,
        .get_scl = stm32_i2c_get_scl,
        .udelay = stm32_i2c_udelay,
        .delay_us = 2,
        .timeout = 100,
};

struct rt_i2c_bus_device i2c1_bus_device;
#endif

void stm32_init_i2c_gpio(struct stm32_i2c_gpio_t *gpio)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_Pin = gpio->pin;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;

    GPIO_Init(gpio->gpio, &GPIO_InitStructure);
    GPIO_Set(gpio);
}

static int stm32_i2c_register(void)
{
#ifdef RT_USING_I2C1
    stm32_init_i2c_gpio(&i2c1_gpio.sda);
    stm32_init_i2c_gpio(&i2c1_gpio.scl);

    rt_memset((void *)&i2c1_bus_device, 0, sizeof(struct rt_i2c_bus_device));

    i2c1_bus_device.priv = (void *)&i2c1_gpio_bit_ops;
    rt_i2c_bit_add_bus(&i2c1_bus_device, "i2c1_bus");
#endif
    return 0;
}
INIT_BOARD_EXPORT(stm32_i2c_register);
